package org.cagt.standard;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;


/**
 * Standard card comparator suitable to use in most situations - have few
 * configuration options given in constructor.
 * 
 * @author konrad.szalkowski (at) gmail.com
 * 
 */
public class StandardCardMultiComparator implements Comparator<StandardCard> {
	private List<Figure> figureSequence;
	private List<Color> colorSequence;
	private boolean colorsBeforeFigures;
	private boolean jokerEqualToAny;

	/**
	 * Standard constructor which contains also all the configuration options. <BR/>
	 * 
	 * @param figureSequence
	 *            Figure sequence - if provided determines the comparation
	 *            result. Lhs figure.index - rhs figure.index. Otherwise not
	 *            index but figure.ordinal will be taken into consideration.
	 * @param colorSequence
	 *            Color sequence - if provided determines the comparation
	 *            result. Lhs color.index - rhs color.index. Otherwise all
	 *            colors are equal. Figures are taken in consideration.
	 * @param colorsBeforeFigures
	 *            If both sequences are provided - determines which is taken
	 *            first in consideration - if true - colors, otherwise figures.
	 * @param jokerEqualToAny
	 *            If set - jokers will be equal to all other cards, otherwise
	 *            jokers will be considered as their position in figureSequence.
	 */
	public StandardCardMultiComparator(List<Figure> figureSequence,
			List<Color> colorSequence, boolean colorsBeforeFigures,
			boolean jokerEqualToAny) {
		// Start terms
		if (colorsBeforeFigures && colorSequence == null) {
			throw new InstantiationError(
					"Colors sequence is required when demanding the colorsBeforeFigures.");
		}
		// Joker terms
		if (jokerEqualToAny && figureSequence != null
				&& !figureSequence.contains(Figure.Joker)) {
			throw new InstantiationError(
					"Joker is required to be in figure sequence when demanding jokerEqualToAny (its position therefore does not matter).");
		}
		this.figureSequence = figureSequence != null ? Collections
				.unmodifiableList(new ArrayList<Figure>(figureSequence)) : null;
		this.colorSequence = colorSequence != null ? Collections
				.unmodifiableList(new ArrayList<Color>(colorSequence)) : null;
		this.colorsBeforeFigures = colorsBeforeFigures;
		this.jokerEqualToAny = jokerEqualToAny;
	}

	public int compare(StandardCard lhs, StandardCard rhs) {
		// Starting terms
		if (lhs == null || rhs == null) {
			throw new NullPointerException();
		}
		// Other terms
		if (figureSequence != null
				&& (!figureSequence.contains(lhs.getFigure()) || !figureSequence
						.contains(rhs.getFigure()))) {
			throw new ComparatorException(
					"Figure sequence present, but figure not in it.");
		}
		if (colorSequence != null
				&& (!colorSequence.contains(lhs.getColor()) || !colorSequence
						.contains(rhs.getColor()))) {
			throw new ComparatorException(
					"Color sequence present, but color not in it.");
		}
		// Compute the values for lhs and rhs
		// Check joker
		if (jokerEqualToAny
				&& (lhs.getFigure() == Figure.Joker || rhs.getFigure() == Figure.Joker)) {
			return 0;
		}
		return getCardValue(lhs) - getCardValue(rhs);
	}

	/**
	 * Returns the theoretical value of card.
	 * 
	 * @param card
	 *            Card.
	 * @return Card value.
	 */
	public int getCardValue(StandardCard card) {
		int lhsF = (figureSequence != null) ? figureSequence.indexOf(card
				.getFigure()) : card.getFigure().ordinal();
		int lhsC = (colorSequence != null) ? colorSequence.indexOf(card
				.getColor()) : 0;
		return colorsBeforeFigures ? lhsC * 100 + lhsF : lhsF * 10 + lhsC;
	}

	/**
	 * Exception thrown if there is something not right when comparing two
	 * cards.
	 * 
	 * @author konrad.szalkowski (at) gmail.com
	 * 
	 */
	public class ComparatorException extends RuntimeException {

		private static final long serialVersionUID = 1L;

		public ComparatorException(String string) {
		}

	}
}
